<?php
// +----------------------------------------------------------------------
// | 在我们年轻的城市里，没有不可能的事！
// +----------------------------------------------------------------------
// | Copyright (c) 2020 http://utils All rights reserved.
// +----------------------------------------------------------------------
// | Author : Jansen <6206574@qq.com>
// +----------------------------------------------------------------------
namespace jansen\utils\validator;
class License{
    /**
     * 是否中国身份证号
     * @param string $number
     * @return bool
     */
    final public static function isIdCard(string $number){
        $number = strtoupper($number);
        //加权因子 
        $wi = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
        //校验码串 
        $ai = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
        //按顺序循环处理前17位 
        $sigma = 0;
        for ($i=0; $i<17; $i++) {
            //提取前17位的其中一位，并将变量类型转为实数 
            $b = $number[$i];
            //提取相应的加权因子 
            $w = $wi[$i];
            //把从身份证号码中提取的一位数字和加权因子相乘，并累加 
            $sigma += $b * $w;
        }
        //计算序号 
        $snumber = $sigma % 11;
        //按照序号从校验码串中提取相应的字符。 
        $check_number = $ai[$snumber];
        return ($number[17] == $check_number) ? true : false;
    }
    /**
     * 是否统一社会信用代码
     *
     * 统一社会信用代码是新的全国范围内唯一的、始终不变的法定代码标识。
     * 由18位数字（或大写拉丁字母）组成
     * 第一位是           登记部门管理代码
     * 第二位是           机构类别代码
     * 第三位到第八位是   登记管理机关行政区域码
     * 第九位到第十七位   主体标识码（组织机构代码）
     * 第十八位           校验码
     * 校验码按下列公式计算：
     * C18 = 31 - MOD ( ∑Ci * Wi ，31) (1)
     * MOD  表示求余函数；
     * i    表示代码字符从左到右位置序号；
     * Ci   表示第i位置上的代码字符的值，采用附录A“代码字符集”所列字符；
     * C18  表示校验码；
     * Wi   表示第i位置上的加权因子，其数值如下表：
     * i 1 2 3 4  5  6  7  8  9  10 11 12 13 14 15 16 17
     * Wi 1 3 9 27 19 26 16 17 20 29 25 13  8 24 10 30 28
     * 当MOD函数值为0（即 C18 = 31）时，校验码用数字0表示。
     *
     * @param string $number
     * @return bool
     */
    public static function isUnifiedSocialCreditCode(string $number){
        $number = strtoupper($number);
        if (strlen($number) != 18)  return false;
        $firstCode = array('1','2','3','4','5','6','7','8','9','A','N','Y');//第一位可以出现的字符
        $secondCode = array('1','2','3','4','5','9');//第二位可以出现的字符
        $weightFactor = array(1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28);//加权因子数值
        $enableCodes =array('0'=>0, '1'=>1, '2'=>2, '3'=>3, '4'=>4, '5'=>5, '6'=>6, '7'=>7, '8'=>8, '9'=>9, 'A'=>10, 'B'=>11, 'C'=>12, 'D'=>13, 'E'=>14, 'F'=>15, 'G'=>16, 'H'=>17, 'J'=>18, 'K'=>19, 'L'=>20, 'M'=>21, 'N'=>22, 'P'=>23, 'Q'=>24, 'R'=>25, 'T'=>26, 'U'=>27, 'W'=>28, 'X'=>29, 'Y'=>30);//信用代码中所有可允许出现的字符及其数值映射表
        $firstCodeOfNumber = substr($number, 0, 1);//信用代码的第一位
        $secondCodeOfNumber = substr($number, 1, 1);//信用代码的第二位
        $majorOfNumber = substr($number, 0, 17);//信用代码的主体部分
        $checkCodeOfNumber = substr($number, -1, 1);//信用代码的校验码
        if (!in_array($firstCodeOfNumber, $firstCode) || !in_array($secondCodeOfNumber, $secondCode))   return false;
        $num = 0;
        for ($i=0; $i<17; $i++) {
            $num +=$enableCodes[$majorOfNumber[$i]]*$weightFactor[$i];
        }
        $result = $num%31?(31-$num%31):0;
        $flipEnableCodes = array_flip($enableCodes);
        return $checkCodeOfNumber == $flipEnableCodes[$result];
    }
}